// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package syntax

import (
	"fmt"
	"io"
	"os"
)

// 模式描述解析器模式。
type Mode uint

// 解析器支持的模式。
const (
	CheckBranches Mode = 1 << iota // 检查标签、中断、继续和转到语句的正确使用
	AllowGenerics
)

// 错误描述语法错误。Error实现了Error接口。
type Error struct {
	Pos Pos
	Msg string
}

func (err Error) Error() string {
	return fmt.Sprintf("%s: %s", err.Pos, err.Msg)
}

var _ error = Error{} // 验证错误是否实现了错误

// 读取.go文件时遇到的每个错误都会调用ErrorHandler。
type ErrorHandler func(err error)

// Pragma值扩充包、导入、常量、func、类型或变量声明。
// 它的意义完全取决于布拉格·马汉德勒，
// 除了nil的意思是“看不到布拉格语”
type Pragma interface{}

// PragmaHandler用于处理
// 它被传递当前pragma值，该值从零开始，
// 并返回一个更新的pragma值。
// 文本是指令，带有“
// 当前pragma保存在每个包、导入、常量、func、类型或变量中
// 声明，放入文件、ImportDecl、ConstDecl、FuncDecl、TypeDecl或VarDecl节点。
// None
// 如果text是空字符串，则返回pragma
// 对于未使用的处理程序，意味着它出现在非声明之前。
// 处理程序可能希望报告错误。在这种情况下，pos是
// 当前解析器位置，而不是pragma本身的位置。
// Blank指定该行在pragma之前是否为空。
type PragmaHandler func(pos Pos, blank bool, text string, current Pragma) Pragma

// Parse从src解析单个Go源文件并返回相应的
// 语法树。如果有错误，Parse将返回找到的第一个错误，
// 和可能部分构造的语法树，或nil。
// None
// 如果呃！=nil，则在遇到每个错误时调用它，并将解析
// 处理尽可能多的源。在本例中，返回的语法树
// 只有在未找到正确的package子句时才为nil。
// 如果errh为nil，解析将在遇到第一个错误时立即终止
// 错误，返回的语法树为nil。
// None
// 如果布拉格！=nil，使用遇到的每个pragma调用它。
// None
func Parse(base *PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) {
	defer func() {
		if p := recover(); p != nil {
			if err, ok := p.(Error); ok {
				first = err
				return
			}
			panic(p)
		}
	}()

	var p parser
	p.init(base, src, errh, pragh, mode)
	p.next()
	return p.fileOrNil(), p.first
}

// ParseFile的行为类似于Parse，但它从命名文件中读取源代码。
func ParseFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
	f, err := os.Open(filename)
	if err != nil {
		if errh != nil {
			errh(err)
		}
		return nil, err
	}
	defer f.Close()
	return Parse(NewFileBase(filename), f, errh, pragh, mode)
}
